/**
 *
 * © Bahadir Zeren 2011
 * bzeren@gmail.com
 * No warranties, express or implied, are made for this program.
 *
 */

package org.baatar.opt.genetic.test.xx;

import java.util.Arrays;
import org.baatar.opt.genetic.chromosome.IChromosome;
import org.baatar.util.RandomUtil;

/**
 *
 * IChromosome implementation for parameter optimizer.
 * Instances of this class are composed of character gene values.
 * These char values are evaluated and used in the code like binary 1, 0 
 * values.
 * 
 */
public class ChromosomeMinXXSquare implements IChromosome<Character> {

    private ChromosomeFactoryMinXXSquare chromosomeFactory = null;
    private int chromosomeLength = 0;

    private char[] genes = null;
    private double fitness = 0.0;
    private double x = 0.0;
    private double y = 0.0;

    public ChromosomeMinXXSquare(ChromosomeFactoryMinXXSquare chromosomeFactory) {
        this.chromosomeFactory = chromosomeFactory;
        this.chromosomeLength = chromosomeFactory.getChromosomeLength();
        genes = new char[chromosomeLength];
    }

    @Override
    public void initializeChromosome() {
    /*
     * 
     *  Initialize
     * 
     */
        for (int i = 0; i < chromosomeLength; i++)
            if (RandomUtil.getRandDouble() > 0.5)
                genes[i] = '1';
            else
                genes[i] = '0';

        calculateFitness();
    }

/*
 * Min + ((Max-Min) / (2^bit - 1)) * intdeger
 *
 * 0 + ((2.5 - 0) / (2^24 - 1)) intdeger
 *
 */
    @Override
    public void calculateFitness() {

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < chromosomeLength / 2; i++) {
            sb.append(genes[i]);
        }

        double paramValue = chromosomeFactory.getMinValue() +
                            ((chromosomeFactory.getMaxValue() -
                                chromosomeFactory.getMinValue()) /
                                (Math.pow(2, chromosomeLength / 2) - 1)) *
                                Long.parseLong(sb.toString(), 2);

        x = paramValue;

        sb = new StringBuilder();
        for (int i = chromosomeLength / 2; i < chromosomeLength; i++) {
            sb.append(genes[i]);
        }

        paramValue = chromosomeFactory.getMinValue() +
                        ((chromosomeFactory.getMaxValue() -
                            chromosomeFactory.getMinValue()) /
                            (Math.pow(2, chromosomeLength / 2) - 1)) *
                            Long.parseLong(sb.toString(), 2);

        y = paramValue;

        /**
         * Fitness function calculation
         */
        fitness = Math.pow(x - 50, 2) + Math.pow(y - 80, 2);
    }

    @Override
    public boolean isEqual(IChromosome cand) {
        ChromosomeMinXXSquare candidate = (ChromosomeMinXXSquare) cand;
        return Arrays.equals(this.genes, candidate.genes);
    }

    @Override
    public double getFitness() {
        return fitness;
    }

    @Override
    public Object getSolution() {
        return toString();
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        ChromosomeMinXXSquare chromosome = (ChromosomeMinXXSquare) super.clone();
        chromosome.genes = chromosome.genes.clone();
        return chromosome;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(genes)
          .append("; ")
          .append("Fithness = ")
          .append(Math.round(fitness * 100000.0) / 100000.0)
          .append("; ")
          .append("x = ")
          .append(Math.round(x * 100000.0) / 100000.0)
          .append("; ")
          .append("y = ")
          .append(Math.round(y * 100000.0) / 100000.0)
          .append("; ");
        return sb.toString();
    }

    @Override
    public Character getGeneValue(int index) {
        return genes[index];
    }

    @Override
    public void setGeneValue(int index, Character value) {
        genes[index] = value;
    }

    /**
     * @return the chromosomeLength
     */
    @Override
    public int getChromosomeLength() {
        return chromosomeLength;
    }
}
